package cn.initcap.algorithm.swing.box.optional1;

import cn.initcap.algorithm.swing.AlgoVisHelper;
import cn.initcap.algorithm.swing.BaseAlgoFrame;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.SwingUtilities;

/**
 * @author initcap
 * @date 8/1/19 6:50 PM
 */
public class AlgoVisualizer {

    private static int DELAY = 5;
    private static int blockSide = 80;
    private GameData data;
    private BaseAlgoFrame frame;
    private Position clickPos1 = null;
    private Position clickPos2 = null;
    private ArrayList<Color> colorList;
    private HashMap<Character, Color> colorMap;

    public AlgoVisualizer(String filename) {

        colorList = new ArrayList<>();
        colorList.add(AlgoVisHelper.RED);
        colorList.add(AlgoVisHelper.PURPLE);
        colorList.add(AlgoVisHelper.BLUE);
        colorList.add(AlgoVisHelper.TEAL);
        colorList.add(AlgoVisHelper.LIGHT_GREEN);
        colorList.add(AlgoVisHelper.LIME);
        colorList.add(AlgoVisHelper.AMBER);
        colorList.add(AlgoVisHelper.DEEP_ORANGE);
        colorList.add(AlgoVisHelper.BROWN);
        colorList.add(AlgoVisHelper.BLUE_GREY);

        colorMap = new HashMap<>();

        data = new GameData(filename);
        int sceneWidth = data.M() * blockSide;
        int sceneHeight = data.N() * blockSide;

        EventQueue.invokeLater(() -> {
            frame = new BaseAlgoFrame("Move the Box Solver", sceneWidth, sceneHeight) {
                @Override
                public void draw(Graphics graphics) {
                    Graphics2D g2d = (Graphics2D) graphics;
                    int w = sceneWidth / data.M();
                    int h = sceneHeight / data.N();

                    Board showBoard = data.getShowBoard();

                    for (int i = 0; i < showBoard.N(); i++) {
                        for (int j = 0; j < showBoard.M(); j++) {
                            char c = showBoard.getData(i, j);
                            if (c != Board.EMPTY) {

                                if (!colorMap.containsKey(c)) {
                                    int sz = colorMap.size();
                                    colorMap.put(c, colorList.get(sz));
                                }

                                Color color = colorMap.get(c);
                                AlgoVisHelper.setColor(g2d, color);
                                AlgoVisHelper.fillRectangle(g2d, j * w + 2, i * h + 2, w - 4, h - 4);

                                AlgoVisHelper.setColor(g2d, AlgoVisHelper.WHITE);
                                String text = String.format("( %d , %d )", i, j);
                                AlgoVisHelper.drawText(g2d, text, j * h + h / 2, i * w + w / 2);
                            }

                            if (i == data.clickx && j == data.clicky) {
                                AlgoVisHelper.setColor(g2d, AlgoVisHelper.LIGHT_BLUE);
                                AlgoVisHelper.setStrokeWidth(g2d, 4);
                                AlgoVisHelper.strokeRectangle(g2d, j * w + 2, i * h + 2, w - 4, h - 4);
                            }
                        }
                    }
                }
            };
            frame.addMouseListener(new AlgoMouseListener());
            new Thread(() -> {
                run();
            }).start();
        });
    }

    public static void main(String[] args) {

        String filename = "level/boston_09.txt";

        new AlgoVisualizer(filename);
    }

    private void run() {

        setData(-1, -1);

        if (data.solve()) {
            System.out.println("The game has a solution!");
        } else {
            System.out.println("The game does NOT have a solution.");
        }

        setData(-1, -1);
    }

    private void setData(int clickx, int clicky) {
        data.clickx = clickx;
        data.clicky = clicky;

        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }

    public void addAlgoMouseListener() {
        frame.addMouseListener(new AlgoMouseListener());
    }

    private class AlgoMouseListener extends MouseAdapter {

        @Override
        public void mouseReleased(MouseEvent event) {
            event.translatePoint(
                    -(int) (frame.getBounds().width - frame.getCanvasWidth()),
                    -(int) (frame.getBounds().height - frame.getCanvasHeight())
            );

            Point pos = event.getPoint();
            //System.out.println(pos.x + " , " + pos.y );

            int w = frame.getCanvasWidth() / data.M();
            int h = frame.getCanvasHeight() / data.N();

            int x = pos.y / h;
            int y = pos.x / w;
            //System.out.println(x + " , " + y);

            if (SwingUtilities.isLeftMouseButton(event)) {
                if (data.inArea(x, y)) {
                    setData(x, y);
                    if (clickPos1 == null) {
                        clickPos1 = new Position(x, y);
                    } else {
                        clickPos2 = new Position(x, y);
                        if (clickPos2.nextTo(clickPos1)) {
                            data.getShowBoard().swap(clickPos1.getX(), clickPos1.getY(), clickPos2.getX(), clickPos2.getY());
                            data.getShowBoard().run();
                        }
                        clickPos1 = null;
                        clickPos2 = null;
                        setData(-1, -1);
                    }
                } else {
                    setData(-1, -1);
                    clickPos1 = null;
                    clickPos2 = null;
                }
            }
        }
    }
}
